package io.hellsinger.vortex.ui.screen.storage.list

import android.Manifest
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Gravity.CENTER
import android.view.View
import android.view.View.VISIBLE
import android.view.ViewGroup.GONE
import android.view.ViewGroup.MarginLayoutParams
import android.view.WindowInsets
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.hellsinger.navigation.screen.ScreenController
import io.hellsinger.theme.vortex.VortexSettings
import io.hellsinger.theme.vortex.formatter
import io.hellsinger.viewmodel.BitViewModel
import io.hellsinger.viewmodel.BitViewModel.UiStateUpdateCollector
import io.hellsinger.vortex.R
import io.hellsinger.vortex.bit.Bits.Companion.hasFlag
import io.hellsinger.vortex.bit.Bits.Companion.hasFlags
import io.hellsinger.vortex.contract.Contracts
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.name
import io.hellsinger.vortex.data.repository.AndroidStorageRepository
import io.hellsinger.vortex.di.createListViewModel
import io.hellsinger.vortex.foundation.isAtLeastAndroid11
import io.hellsinger.vortex.foundation.util.Counter
import io.hellsinger.vortex.navigation.globalUiProvider
import io.hellsinger.vortex.ui.component.InfoView
import io.hellsinger.vortex.ui.component.LoadingView
import io.hellsinger.vortex.ui.component.MoreAction
import io.hellsinger.vortex.ui.component.ProvideFullStorageAccessAction
import io.hellsinger.vortex.ui.component.ProvideStorageAccessAction
import io.hellsinger.vortex.ui.component.SearchAction
import io.hellsinger.vortex.ui.component.TasksAction
import io.hellsinger.vortex.ui.component.adapter.listener.ItemListener
import io.hellsinger.vortex.ui.component.dp
import io.hellsinger.vortex.ui.component.dsl.trail
import io.hellsinger.vortex.ui.component.item.menu.MenuActionListener
import io.hellsinger.vortex.ui.component.linear
import io.hellsinger.vortex.ui.component.navigation.NavigationBarView
import io.hellsinger.vortex.ui.component.observeInsets
import io.hellsinger.vortex.ui.component.storage.StorageAdapter
import io.hellsinger.vortex.ui.component.storage.StorageSnackBar
import io.hellsinger.vortex.ui.component.storage.standard.linear.StandardStorageLinearCell
import io.hellsinger.vortex.ui.component.trail.TrailAdapter
import io.hellsinger.vortex.ui.component.trail.TrailItemView
import io.hellsinger.vortex.ui.component.trail.TrailListView
import io.hellsinger.vortex.ui.dsl.frame
import io.hellsinger.vortex.ui.dsl.frameParams
import io.hellsinger.vortex.ui.dsl.info
import io.hellsinger.vortex.ui.dsl.loading
import io.hellsinger.vortex.ui.dsl.recycler
import io.hellsinger.vortex.ui.icon.Icons
import io.hellsinger.vortex.ui.screen.storage.editor.StorageFileEditorScreenController
import io.hellsinger.vortex.ui.screen.storage.list.StorageListScreenViewModel.Companion.REASON_STORAGE_FULL_PERMISSION
import io.hellsinger.vortex.ui.screen.storage.list.StorageListScreenViewModel.Companion.REASON_STORAGE_PERMISSION
import io.hellsinger.vortex.ui.screen.storage.media.StorageMediaScreenController
import io.hellsinger.vortex.util.hasImageSupportableFormat
import kotlin.math.roundToInt

class StorageListScreenController(
    private val context: Context,
) : ScreenController<Unit>(),
    View.OnApplyWindowInsetsListener,
    UiStateUpdateCollector<UiState>,
    ItemListener,
    StorageListScreenViewModel.ItemStateChangeListener,
    StorageListScreenViewModel.FileOperationProgressListener,
    MenuActionListener {
    private var viewModel: StorageListScreenViewModel? = null

    private val bar: NavigationBarView? = globalUiProvider(context)?.bar

    private var container: FrameLayout? = null
    private var recycler: RecyclerView? = null
    private var snack: StorageSnackBar? = null
    private var trail: TrailListView? = null
    private var loading: LoadingView? = null
    private var info: InfoView? = null
    private val counter = Counter(initial = 0)
    private var animator: ValueAnimator? = null

    private val trailAdapter: TrailAdapter =
        TrailAdapter(
            listener = this,
            viewProvider = { parent, _ ->
                TrailItemView(parent.context).apply {
                    setBackgroundColor(color = VortexSettings.colors { key_storage_list_trail_surface_color })
                    setTitleColors(
                        normal = VortexSettings.colors { key_storage_list_trail_item_text_color },
                        selected = VortexSettings.colors { key_storage_list_trail_item_text_selected_color },
                    )
                    setArrowColors(
                        normal = VortexSettings.colors { key_storage_list_trail_item_arrow_color },
                        selected = VortexSettings.colors { key_storage_list_trail_item_arrow_selected_color },
                    )
                    setRippleColors(
                        normal = VortexSettings.colors { key_storage_list_trail_item_ripple_color },
                        selected = VortexSettings.colors { key_storage_list_trail_item_arrow_selected_color },
                    )
                }
            },
        )
    private val adapter =
        StorageAdapter(
            listener = this,
            isSelected = { item -> viewModel?.isSelected(item) == true },
            viewProvider = { parent, _ ->
                StandardStorageLinearCell(parent.context).apply {
                    setBackgroundColor(VortexSettings.colors { key_storage_list_item_surface_color })
                    setTitleColor(VortexSettings.colors { key_storage_list_item_title_color })
                    setSubtitleColor(VortexSettings.colors { key_storage_list_item_subtitle_color })
                    setIconColor(VortexSettings.colors { key_storage_list_item_icon_color })
                    setIconSurfaceColor(VortexSettings.colors { key_storage_list_item_icon_surface_color })
                    setSurfaceRippleColor(VortexSettings.colors { key_storage_list_item_surface_ripple_color })
                    setIconSurfaceRippleColor(VortexSettings.colors { key_storage_list_item_surface_ripple_color })
                    setSelectionColor(VortexSettings.colors { key_storage_list_item_selected_color })
                }
            },
        )

    private val selected: List<PathItem>?
        get() = viewModel?.getUiState()?.selected

    private inline fun checkParent(
        context: Context,
        block: FrameLayout.() -> Unit,
    ): FrameLayout {
        if (container == null) {
            container =
                frame(context) {
                    setBackgroundColor(VortexSettings.colors { key_storage_list_background_color })
                    frameParams(MATCH_PARENT, MATCH_PARENT)
                    clipChildren = false
                }
        }

        return container!!.apply(block)
    }

    override fun onCreateViewImpl(context: Context): View =
        checkParent(context) {
            recycler =
                recycler(0) {
                    id = R.id.file_manager_list_root
                    frameParams(MATCH_PARENT, MATCH_PARENT)
                    adapter = this@StorageListScreenController.adapter
                    layoutManager = LinearLayoutManager(context)
//            itemAnimator = null
                    setHasFixedSize(true)
                    isNestedScrollingEnabled = true
                    clipToPadding = false
                    visibility = GONE
                    observeInsets(this@StorageListScreenController)

                    addOnScrollListener(
                        object : RecyclerView.OnScrollListener() {
                            override fun onScrolled(
                                parent: RecyclerView,
                                dx: Int,
                                dy: Int,
                            ) {
                                (parent.layoutManager as? LinearLayoutManager)?.findFirstCompletelyVisibleItemPosition()
                                if (dy > 0) bar?.hide() else bar?.show()
                                parent.linear {
                                    if (findFirstCompletelyVisibleItemPosition() == 0) {
                                        trail?.elevation = 0F
                                    } else {
                                        trail?.elevation = 4F.dp
                                    }
                                }
                            }

                            override fun onScrollStateChanged(
                                parent: RecyclerView,
                                newState: Int,
                            ) {
                            }
                        },
                    )
                }

            info =
                info(1) {
                    frameParams(MATCH_PARENT, MATCH_PARENT)
                    setIconColor(VortexSettings.colors { key_storage_list_info_icon_color })
                    setMessageTextColor(VortexSettings.colors { key_storage_list_info_message_color })
                    visibility = GONE
                }

            trail =
                trail(2) {
                    id = R.id.file_manager_list_trail
                    frameParams(MATCH_PARENT, WRAP_CONTENT)
                    adapter = trailAdapter
                    itemAnimator = null
                    setBackgroundColor(VortexSettings.colors { key_storage_list_trail_surface_color })
                    observeInsets(this@StorageListScreenController)
                }

            loading =
                loading(3) {
                    setTitleColor(VortexSettings.colors { key_storage_list_loading_title_color })
                    setProgressColor(VortexSettings.colors { key_storage_list_loading_progress_color })
                    frameParams(MATCH_PARENT, MATCH_PARENT) {
                        gravity = CENTER
                    }
                    visibility = GONE
                }
        }

    override fun onApplyWindowInsets(
        v: View,
        insets: WindowInsets,
    ): WindowInsets {
        val compat = WindowInsetsCompat.toWindowInsetsCompat(insets)
        val statusBarInsets = compat.getInsets(Type.statusBars())
        val navigationBarInsets = compat.getInsets(Type.navigationBars())
        when (v.id) {
            R.id.file_manager_list_trail -> v.updatePadding(top = statusBarInsets.top)

            R.id.file_manager_list_root ->
                v.updatePadding(
                    top = context.dp(40F).roundToInt() + statusBarInsets.top,
                    bottom = navigationBarInsets.bottom,
                )

            R.id.file_manager_list_snack_bar_root -> {
                v.updateLayoutParams<MarginLayoutParams> {
                    bottomMargin = navigationBarInsets.bottom + context.dp(4F).roundToInt()
                }
            }
        }
        return insets
    }

    override fun onItemClick(
        view: View,
        position: Int,
    ) {
        when (view.id) {
            R.id.file_manager_list_item_root -> {
                val item = viewModel?.getUiState()?.items?.get(position) ?: return
                if (item.isDirectory) {
                    viewModel?.navigate(item)
                } else {
                    if (item.hasImageSupportableFormat()) {
                        val images =
                            viewModel?.getUiState()?.items?.filter { it.hasImageSupportableFormat() }
                                ?: return
                        router?.navigate(
                            screen = StorageMediaScreenController(context),
                            args =
                                StorageMediaScreenController.Items(
                                    sources = images,
                                    selected = images.indexOf(item),
                                ),
                        )
                    } else {
                        router?.navigate(
                            screen = StorageFileEditorScreenController(context),
                            args = item,
                        )
                    }
                }
            }

            R.id.file_manager_list_item_icon -> viewModel?.toggleSelection(position)
            R.id.file_manager_list_trail_item_root -> {
                if (position == viewModel?.getUiState()?.selectedTrailItemPosition) {
                    recycler?.smoothScrollToPosition(0)
                } else {
                    viewModel?.navigateFromTrail(position)
                }
            }
        }
    }

    override fun onLongItemClick(
        view: View,
        position: Int,
    ): Boolean {
        when (view.id) {
            R.id.file_manager_list_item_root -> {
                if (viewModel?.isSelected(position) == true) {
                    router?.navigate(
                        screen = StorageListMenuScreenController(context),
                        args = viewModel?.getUiState()?.selected,
                    )
                } else {
                    viewModel?.select(position)
                }
                return true
            }

            R.id.file_manager_list_item_icon -> {
                val item = viewModel?.getUiState()?.items?.get(position) ?: return true
                router?.navigate(
                    screen = StorageListMenuScreenController(context),
                    args = listOf(item),
                )
                return true
            }

            R.id.file_manager_list_trail_item_root -> {
                val item = viewModel?.getUiState()?.trail?.get(position) ?: return true
                router?.navigate(
                    screen = StorageListMenuScreenController(context),
                    args = listOf(item),
                )
                return true
            }
        }
        return false
    }

    // TODO: Replace with UiState interface for better readability and extensibility
    override suspend fun emit(
        event: Int,
        state: UiState,
    ) {
        if (event == BitViewModel.IDLE) return

        if (event hasFlag StorageListScreenViewModel.UPDATE_SELECTION_COUNT) {
            counter.replace(state.selected.size)
            wrapBar()
        }

        if (event hasFlag StorageListScreenViewModel.UPDATE_LIST) {
            if (recycler?.isVisible == false) {
                animator?.cancel()
                createEventAnimator()
            }
            animator?.addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationCancel(animation: Animator) {
                        recycler?.visibility = VISIBLE
                        recycler?.alpha = 1F
                        loading?.visibility = GONE
                        info?.visibility = GONE
                        animator = null
                    }

                    override fun onAnimationStart(animation: Animator) {
                        recycler?.visibility = GONE
                    }

                    override fun onAnimationEnd(animation: Animator) {
                        recycler?.visibility = VISIBLE
                        recycler?.alpha = 1F
                        loading?.visibility = GONE
                        info?.visibility = GONE
                        animator = null
                    }
                },
            )
            animator?.addUpdateListener { animator ->
                if (loading?.isVisible == true) loading?.alpha = 1F - animator.animatedFraction
                if (info?.isVisible == true) info?.alpha = 1F - animator.animatedFraction
                recycler?.alpha = animator.animatedFraction
            }

            adapter.replace(state.items)
        }

        if (event hasFlag StorageListScreenViewModel.UPDATE_TRAIL_LIST) {
            trailAdapter.replace(state.trail)
            wrapBar()
            trailAdapter.updateSelected(state.selectedTrailItemPosition)
        }

        if (event hasFlag StorageListScreenViewModel.UPDATE_TRAIL_SELECTED_CHANGED) {
            wrapBar()
            trailAdapter.updateSelected(state.selectedTrailItemPosition)
        }

        if (event hasFlag StorageListScreenViewModel.UPDATE_LOADING_PROGRESS) {
            if (loading?.isVisible == false) {
                animator?.cancel()
                createEventAnimator()
            }
            animator?.addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationCancel(animation: Animator) {
                        loading?.visibility = VISIBLE
                        loading?.alpha = 1F
                        info?.visibility = GONE
                        recycler?.visibility = GONE
                        animator = null
                    }

                    override fun onAnimationStart(animation: Animator) {
                        loading?.visibility = GONE
                    }

                    override fun onAnimationEnd(animation: Animator) {
                        loading?.visibility = VISIBLE
                        loading?.alpha = 1F
                        info?.visibility = GONE
                        recycler?.visibility = GONE
                        animator = null
                    }
                },
            )
            animator!!.addUpdateListener { animator ->
                if (recycler?.isVisible == true) {
                    recycler?.alpha = 1F - animator.animatedFraction
                }
                if (info?.isVisible == true) info?.alpha = 1F - animator.animatedFraction
                loading?.alpha = animator.animatedFraction
            }
        }

        if (event hasFlag StorageListScreenViewModel.UPDATE_MESSAGE) {
            if (info?.isVisible == false) {
                animator?.cancel()
                createEventAnimator()
            }
            animator?.addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationCancel(animation: Animator) {
                        info?.visibility = VISIBLE
                        info?.alpha = 1F
                        loading?.visibility = GONE
                        recycler?.visibility = GONE
                        animator = null
                    }

                    override fun onAnimationStart(animation: Animator) {
                        info?.visibility = GONE
                    }

                    override fun onAnimationEnd(animation: Animator) {
                        info?.visibility = VISIBLE
                        info?.alpha = 1F
                        loading?.visibility = GONE
                        recycler?.visibility = GONE
                        animator = null
                    }
                },
            )
            animator?.addUpdateListener { animator ->
                if (recycler?.isVisible == true) {
                    recycler?.alpha =
                        1F - animator.animatedFraction
                }
                if (loading?.isVisible == true) loading?.alpha = 1F - animator.animatedFraction
                info?.alpha = animator.animatedFraction
            }

            if (event hasFlag StorageListScreenViewModel.REASON_RESTRICTED_ITEM) {
                info?.icon = Icons.Rounded.Directory
                info?.message = VortexSettings.texts { key_storage_list_info_message_restricted }
            } else if (event hasFlag StorageListScreenViewModel.REASON_ITEM_DOES_NOT_EXIST) {
                info?.icon = Icons.Rounded.File
                info?.message = "Entry ${state.selectedTrailItem?.path?.getNameAt()} doesn't exist"
            } else if (event hasFlag StorageListScreenViewModel.REASON_ITEM_IS_NOT_READABLE) {
                info?.icon = Icons.Rounded.File
                info?.message =
                    "Entry ${state.selectedTrailItem?.path?.getNameAt()} is not readable"
            } else if (event hasFlag StorageListScreenViewModel.REASON_EMPTY_ITEMS) {
                info?.icon = Icons.Rounded.Directory
                info?.message =
                    formatter(
                        state.selectedTrailItem
                            ?.path
                            ?.getNameAt()
                            .toString(),
                    ) { key_storage_list_info_message_empty }
            } else if (event hasFlag REASON_STORAGE_PERMISSION) {
                info?.icon = Icons.Rounded.Info
                info?.message = VortexSettings.texts { key_storage_list_info_message_access }
            } else if (event hasFlag REASON_STORAGE_FULL_PERMISSION) {
                info?.icon = Icons.Rounded.Info
                info?.message = VortexSettings.texts { key_storage_list_info_message_full_access }
            }
        }

        if (event.hasFlags(REASON_STORAGE_PERMISSION, REASON_STORAGE_FULL_PERMISSION)) {
            if (isAtLeastAndroid11) {
                bar?.replaceItems(listOf(ProvideFullStorageAccessAction))
            } else {
                bar?.replaceItems(listOf(ProvideStorageAccessAction))
            }
        }

        animator?.start()
    }

    private fun createEventAnimator(duration: Long = DEFAULT_ANIMATOR_DURATION): ValueAnimator {
        if (animator != null) return animator!!

        val animator = ValueAnimator.ofFloat(0F, 1F)

        animator.duration = duration

        this.animator = animator
        return animator
    }

    override fun onRestoreImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean = viewModel?.restore(buffer, prefix) ?: false

    override fun onSaveImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean = viewModel?.save(buffer, prefix) ?: false

    override fun onActivityResultImpl(
        requestCode: Int,
        resultCode: Int,
        data: Intent?,
    ): Boolean {
//        if (resultCode != Activity.RESULT_OK) return false
        return when (requestCode) {
            Contracts.Storage.REQUEST_STORAGE_ACCESS -> {
                viewModel?.checkPermission()
                true
            }

            Contracts.Storage.REQUEST_FULL_STORAGE_ACCESS -> {
                viewModel?.checkPermission()
                true
            }

            else -> false
        }
    }

    override fun onBackActivatedImpl(): Boolean {
        if (viewModel?.getUiState()?.selectedTrailItem?.path != AndroidStorageRepository.EXTERNAL_STORAGE_PATH) {
            viewModel?.navigateBack()
            return true
        }

        if (selected?.isNotEmpty() == true) {
            viewModel?.clearSelection()
            adapter.clearSelection()
            return true
        }

        return false
    }

    override fun onDestroyImpl() {
        onHideImpl()
        viewModel?.onDestroy()
        container?.removeAllViews()
        viewModel = null
        trail = null
        loading = null
        info = null
        container = null
    }

    override fun onFocusImpl() {
    }

    override fun onPrepareImpl() {
        if (viewModel == null) viewModel = context.createListViewModel()

        bar?.replaceItems(listOf(MoreAction, SearchAction, TasksAction))
        bar?.registerListener(this)

        viewModel?.apply {
            collectUiState(this@StorageListScreenController)
            collectPathEvents()
            bindItemStateChangeListener(this@StorageListScreenController)
            bindFileOperationProgressListener(this@StorageListScreenController)
            checkPermission()
        }

        wrapBar()
    }

    override fun onMenuActionCall(id: Int): Boolean =
        when (id) {
            R.id.file_manager_list_tasks_action -> {
                if (viewModel?.hasPendingTasks() == true) {
                    router?.navigate(
                        screen = StorageListMenuTaskScreenController(context),
                        args = viewModel?.getUiState()?.selectedTrailItem,
                    )
                }
                true
            }

            R.id.file_manager_list_provide_full_storage_access_action -> {
                if (context is Activity) {
                    context.startActivityForResult(
                        Contracts.Storage.StorageFullAccessIntent(context),
                        Contracts.Storage.REQUEST_FULL_STORAGE_ACCESS,
                    )
                }
                true
            }

            R.id.file_manager_list_provide_storage_access_action -> {
                if (context is Activity) {
                    context.requestPermissions(
                        arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
                        Contracts.Storage.REQUEST_STORAGE_ACCESS,
                    )
                }
                true
            }

            else -> {
                false
            }
        }

    override fun onHideImpl() {
        viewModel?.cancelCollectUiState()
        viewModel?.unbindItemStateChangeListener()
        bar?.unregisterListener(this)
    }

    override fun onSelect(position: Int) {
        adapter.applySelect(position)
    }

    override fun onDeselect(position: Int) {
        adapter.applyDeselect(position)
    }

    override fun handleMessage(
        message: String,
        progress: Long,
        max: Long,
    ) {
        snack?.apply {
            title = message
            updateProgress(progress / max.toFloat())
        }
    }

    private fun wrapBar() {
        if (!counter.isZero) {
            bar?.setTitleWithAnimation(
                title = counter.value.toString(),
                isReverse = counter.isBiggerThanLast,
            )
        } else {
            bar?.setTitleWithAnimation(
                title = viewModel?.getUiState()?.selectedTrailItem?.name,
                isReverse =
                    trailAdapter.selected < (
                        viewModel?.getUiState()?.selectedTrailItemPosition
                            ?: -1
                    ),
            )
        }
        bar?.show()
    }

    companion object {
        private const val DEFAULT_ANIMATOR_DURATION = 150L
    }
}
